﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

namespace LibrarySync2.Common.Xml
{
    public static class XmlExtensionMethods
    {
        public static Func<string, int?> ParseInt = s => { int result; return Int32.TryParse(s, out result) ? result : (int?)null; };
        public static Func<string, long?> ParseLong = s => { long result; return Int64.TryParse(s, out result) ? result : (long?)null; };
        public static Func<string, DateTime?> ParseDateTime = s => { DateTime result; return DateTime.TryParse(s, out result) ? result : (DateTime?)null; };
        public static Func<string, bool?> ParseBoolean = s => { bool result; return Boolean.TryParse(s, out result) ? result : (bool?)null; };

        public static XmlDocument LoadDocument(string path)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");
            var file = new FileInfo(path);
            if (!file.Exists)
                return null;

            var result = new XmlDocument();
            using (var readStream = file.OpenRead())
            {
                result.Load(readStream);
            }

            return result;
        }

        public static string GetSerializedContent(object toSerialize)
        {
            var type = toSerialize.GetType();
            var resultBuilder = new StringBuilder();
            using (var stream = new StringWriter(resultBuilder))
            {
                XmlSerializer serializer = new XmlSerializer(type);
                serializer.Serialize(stream, toSerialize);
            }
            return resultBuilder.ToString();
        }

        public static object GetDeserializedObject(Type type, string serializedContent)
        {
            using (var stream = new StringReader(serializedContent))
            {
                XmlSerializer serializer = new XmlSerializer(type);
                return serializer.Deserialize(stream);
            }
        }

        public static string GetNodeValue(this XmlNode parentNode, string path)
        {
            var node = parentNode.SelectSingleNode(path);
            return (node == null) ?
                string.Empty :
                node.InnerText;
        }

        public static TValue? GetNodeValueAs<TValue>(this XmlNode parentNode, string path, Func<string, TValue?> parse)
            where TValue : struct
        {
            var stringValue = parentNode.GetNodeValue(path);
            return parse(stringValue);
        }

        public static string GetNodeAttributeValue(this XmlNode parentNode, string attributeName)
        {
            var attribute = parentNode.Attributes[attributeName];
            return (attribute == null) ?
                string.Empty :
                attribute.Value;
        }

        public static void Serialize(this object toSerialize, FileInfo outputFile)
        {
            Type t = toSerialize.GetType();
            if (t.IsSerializable)
            {
                if (outputFile.Exists)
                    outputFile.Delete();

                XmlSerializer serializer = new XmlSerializer(t);
                using (TextWriter tw = new StreamWriter(outputFile.OpenWrite()))
                {
                    serializer.Serialize(tw, toSerialize);
                    tw.Close();
                }
            }
            else
            {
                throw new InvalidOperationException(string.Format("Objects of type {0} are not serializable.", t.Name));
            }
        }

        public static TSerialized LoadSerializedContent<TSerialized>(this FileInfo inputFile)
            where TSerialized : class
        {
            if (!inputFile.Exists)
                throw new ArgumentException(string.Format(
                    "Input file '{0}' does not exist", inputFile.FullName));

            Type t = typeof(TSerialized);
            if (!t.IsSerializable)
                throw new InvalidOperationException(string.Format(
                    "Unable to deserialize type '{0}'", t.FullName));

            TSerialized result = null;
            XmlSerializer serializer = new XmlSerializer(t);
            //TODO this tends to thorw an exception when the file is not there. Not sure if that is good. 
            //Seems to die bad if the xml is not 100% correct
            using (TextReader reader = new StreamReader(inputFile.OpenRead()))
            {
                result = serializer.Deserialize(reader) as TSerialized;
            }
            return result;
        }

        public static XmlNode AddNode(this XmlDocument document, XmlNode parentNode, string name, string value)
        {
            var node = document.CreateNode(XmlNodeType.Element, name, string.Empty);
            if (!string.IsNullOrEmpty(value))
                node.AppendChild(document.CreateTextNode(value));
            parentNode.AppendChild(node);
            return node;
        }

        public static XmlNode AddAttribute(this XmlDocument document, XmlNode node, string name, string value)
        {
            var attribute = document.CreateAttribute(name);
            attribute.Value = value;
            node.Attributes.Append(attribute);
            return node;
        }
    }
}
